﻿//========================================================= 
/**@file H002CardDetectorCmdDriver.h
 * @brief H002卡片检测
 * 
 * @date 2016-10-26   21:39:51
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_H002CARDDETECTORCMDDRIVER_H_
#define _LIBZHOUYB_H002CARDDETECTORCMDDRIVER_H_
//--------------------------------------------------------- 
#include "../CommonCmdDriver.h"

#include "../../magnetic/H002MagDevAdapter.h"
using zhou_yb::application::magnetic::H002MagDevAdapter;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace driver {
//--------------------------------------------------------- 
/// H002卡片检测命令驱动
class H002CardDetectorCmdDriver : 
    public CommonCmdDriver, 
    public BaseDevAdapterBehavior<IInteractiveTrans>,
    public InterruptBehavior
{
protected:
    //----------------------------------------------------- 
    /// 磁条口
    ByteBuilder _magGate;
    /// 接触式IC卡口
    ByteBuilder _contactGate;
    /// 非接触式IC卡口
    ByteBuilder _contactlessGate;
    /// 二代证口
    ByteBuilder _idGate;
    /// 设备适配器
    DevAdapterSetter<IInteractiveTrans> _devSetter;
    /// 磁条
    H002MagDevAdapter _mag;
    /// IC卡命令集
    ComICCardCmdAdapter _cmdAdapter;
    /// 接触式IC卡
    ComContactICCardDevAdapter _contactIC;
    /// 非接触式IC卡
    ComContactlessICCardDevAdapter _contactlessIC;
    /// 身份证
    SdtApiDevAdapter _sdtapiAdapter;
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    /// 识别到的卡片类别
    enum CardType
    {
        /// 无卡
        CardNull = 0,
        /// 非接触式IC卡
        CardContactless,
        /// 接触式IC卡
        CardContact,
        /// 磁条卡
        CardMAG,
        /// 身份证
        CardIDC
    };
    /// 将卡片类型枚举转换为字符串的格式
    static string SlotToString(CardType slot)
    {
        switch(slot)
        {
        case CardContactless:
            return "Contactless";
        case CardContact:
            return "Contact";
        case CardMAG:
            return "MAG";
        case CardIDC:
            return "IDC";
        case CardNull:
        default:
            break;
        }
        return "Null";
    }
    /// 将字符串格式的卡槽转换为枚举类型
    static CardType StringToSlot(const char* slot)
    {
        ByteArray sSlot(slot);
        if(StringConvert::Compare(sSlot, "Contact", true))
        {
            return CardContact;
        }
        else if(StringConvert::Compare(sSlot, "Contactless", true))
        {
            return CardContactless;
        }
        else if(StringConvert::Compare(sSlot, "MAG", true))
        {
            return CardMAG;
        }
        else if(StringConvert::Compare(sSlot, "IDC", true))
        {
            return CardIDC;
        }
        return CardNull;
    }
    //----------------------------------------------------- 
    H002CardDetectorCmdDriver() : CommonCmdDriver()
    {
        _errGetter.Add("MAG", _mag)
            .Add("CONTACT", _contactIC)
            .Add("CONTACTLESS", _contactlessIC)
            .Add("SDT", _sdtapiAdapter);

        select_helper<LoggerSetter::SelecterType>::select(_logSetter),
            _cmdAdapter, _mag, _contactIC, _contactlessIC, _sdtapiAdapter;

        select_helper<DevAdapterSetter<IInteractiveTrans>::SelecterType>::select(_devSetter),
            _mag, _cmdAdapter, _sdtapiAdapter;

        _contactIC.SelectDevice(_cmdAdapter);
        _contactlessIC.SelectDevice(_cmdAdapter);

        Ref<ComplexCommand> resetCmd = _RegisteCMD(H002CardDetectorCmdDriver, MagReset);
        _RegisteCMD(H002CardDetectorCmdDriver, PollCard)->Bind(resetCmd, ComplexCommand::RunOnAlways);

        _RegisteExceptCMD(H002CardDetectorCmdDriver);
        _RegisteCMD(H002CardDetectorCmdDriver, SetGate);
        _RegisteCMD(H002CardDetectorCmdDriver, IsCardPresent);
        _RegisteAdapterCMD(H002CardDetectorCmdDriver);
        _RegisteCancelCMD(H002CardDetectorCmdDriver);
    }
    LC_CMD_CANCEL(*this);
    LC_CMD_ADAPTER_INVOKE(IInteractiveTrans, _devSetter);
    LC_CMD_EXCEPT()
    {
        EXCEPT_CMD(SetGate);
        return true;
    }
    /**
     * @brief 设置各个模块的转口指令
     * @date 2016-11-01 16:13
     * 
     * @param [in] Contact : hex 接触式IC卡
     * @param [in] Contactless : hex 非接触式IC卡
     * @param [in] MAG : hex 磁条卡
     * @param [in] IDC : hex 身份证
     */
    LC_CMD_METHOD(SetGate)
    {
        string sGate;
        if(arg.Get("Contact", sGate) || arg.Get("1", sGate))
        {
            _contactGate.Clear();
            DevCommand::FromAscii(sGate.c_str(), _contactGate);
            sGate = "";
        }
        if(arg.Get("Contactless", sGate) || arg.Get("2", sGate))
        {
            _contactlessGate.Clear();
            DevCommand::FromAscii(sGate.c_str(), _contactlessGate);
            sGate = "";
        }
        if(arg.Get("MAG", sGate) || arg.Get("3", sGate))
        {
            _magGate.Clear();
            DevCommand::FromAscii(sGate.c_str(), _magGate);
            sGate = "";
        }
        if(arg.Get("IDC", sGate) || arg.Get("4", sGate))
        {
            _idGate.Clear();
            DevCommand::FromAscii(sGate.c_str(), _idGate);
            sGate = "";
        }
        return true;
    }
    /**
     * @brief 检测设备上是否有卡片
     * @date 2016-11-01 22:40
     * 
     * @retval HasContact : bool 是否有接触式IC卡
     * @retval HasContactless : bool 是否有非接触式IC卡
     * @retval HasMAG : bool 是否刷过磁条卡
     * @retval HasIDC : bool 是否有身份证
     */
    LC_CMD_METHOD(IsCardPresent)
    {
        bool hasContact = false;
        bool hasContactless = false;
        bool hasMag = false;
        bool hasID = false;

        if(!_contactGate.IsEmpty())
            ASSERT_FuncErrInfo(_pDev->Write(_contactGate), DeviceError::SendErr, "转接触口失败");
        if(_contactIC.PowerOn(NULL, NULL))
            hasContact = true;
        
        if(!_contactlessGate.IsEmpty())
            ASSERT_FuncErrInfo(_pDev->Write(_contactlessGate), DeviceError::SendErr, "转非接口失败");

        byte cardType = 0x00;
        if(_contactlessIC.ActiveContactlessICCard(0, &cardType))
        {
            // 检测是否为身份证
            if(cardType == 0x0B)
            {
                if(!_idGate.IsEmpty())
                    ASSERT_FuncErrInfo(_pDev->Write(_idGate), DeviceError::SendErr, "转二代证口失败");
                if(_sdtapiAdapter.FindIDCard())
                {
                    hasID = true;
                }
            }
            else
            {
                hasContactless = true;
            }
        }

        if(!_magGate.IsEmpty())
            ASSERT_FuncErrInfo(_pDev->Write(_magGate), DeviceError::SendErr, "转磁条口失败");
        ByteBuilder trBuff(128);
        DeviceError::ErrorCode err = H002MagDevAdapter::IsMagPresent(_pDev, trBuff);
        if(DeviceError::IsSuccess(err))
            hasMag = true;

        rlt.Put("HasContact", ArgConvert::ToString(hasContact));
        rlt.Put("HasContactless", ArgConvert::ToString(hasContactless));
        rlt.Put("HasMAG", ArgConvert::ToString(hasMag));
        rlt.Put("HasIDC", ArgConvert::ToString(hasID));
        return true;
    }
    /**
     * @brief 清除磁条状态
     *
     * @param [in] IsClearMAG : bool [default:false] 是否清除磁条状态
     */
    LC_CMD_METHOD(MagReset)
    {
        bool isClearMAG = arg["IsClearMAG"].To<bool>(false);
        if(!isClearMAG)
            return true;
        
        if(!_magGate.IsEmpty())
        {
            ASSERT_FuncErrInfo(_pDev->Write(_magGate), DeviceError::SendErr, "转磁条口失败");
        }
        return _mag.MagClear();
    }
    /**
     * @brief 等待放卡
     * @date 2016-10-26 23:01
     * 
     * @param [in] Timeout : uint [default:DEV_WAIT_TIMEOUT] 等待放卡的超时时间(ms)
     * @parma [in] Interval : uint [default:DEV_OPERATOR_INTERVAL] 轮询时的间隔时间(ms);
     * @param [in] FLAG : string [default:ALL] 需要检测的卡片类型,类型出现的顺序决定了轮询时的优先级
     * @warning 支持两种格式:"1234" 或者 "Contact|Contactless|MAG|ID"
     *
     * @param [in] IsAutoMAG : [default:true] 是否自动发送读磁条指令和清磁条状态指令
     * 
     * @retval SLOT : uint 检测到的卡片种类
     * - 类别:
     *  - 0 : 无卡 CardNull
     *  - 1 : 非接触式IC卡 CardContactless
     *  - 2 : 接触式 CardContact
     *  - 3 : 磁条卡 CardMAG
     *  - 4 : 身份证 CardIDC
     * .
     * @retval SLOT_NAME : string 检测到的卡片类别名称
     * - 名称:
     *  - Null        : 无卡
     *  - Contactless : 非接触式IC卡
     *  - Contact     : 接触式IC卡
     *  - MAG         : 磁条卡
     *  - IDC         : 身份证
     *  .
     *
     * @retval ATR : hex 卡片为IC卡时的数据
     * @retval UID : hex 卡片为非接触式IC卡时的数据
     * @retval TrBuff : string 卡片为磁条时的数据
     */
    LC_CMD_METHOD(PollCard)
    {
        uint timeoutMs = arg["Timeout"].To<uint>(DEV_WAIT_TIMEOUT);
        uint intervalMs = arg["Interval"].To<uint>(DEV_OPERATOR_INTERVAL);
        bool isAutoMAG = arg["IsAutoMAG"].To<bool>(true);
        string flag = arg["FLAG"].To<string>("");
        // 将 "ID|MAG|Contact|Contactless" 格式的数据转换为 "1234" 的格式
        if(StringConvert::ContainsChar(flag.c_str(), SPLIT_CHAR))
        {
            list_t<string> cards;
            StringHelper::Split(flag.c_str(), cards, SPLIT_CHAR);

            list_t<string>::iterator itr;
            for(itr = cards.begin();itr != cards.end(); ++itr)
            {
                if(StringConvert::Contains(itr->c_str(), "Contactless", true))
                    flag += "1";
                else if(StringConvert::Contains(itr->c_str(), "Contact", true))
                    flag += "2";
                else if(StringConvert::Contains(itr->c_str(), "MAG", true))
                    flag += "3";
                else if(StringConvert::Contains(itr->c_str(), "IDC", true))
                    flag += "4";
            }
        }
        if(flag.length() < 1)
            flag = "1234";

        byte cardType = 0x00;
        CardType cardFlag = CardNull;
        ByteBuilder atr(32);
        ByteBuilder uid(8);
        ByteBuilder trBuff(64);
        ByteArray flagArray(flag.c_str(), flag.length());

        const char SLOT_KEY[] = "SLOT";
        const char SLOT_NAME_KEY[] = "SLOT_NAME";
        
        // 需要发送读磁条指令
        const char IsClearMagKEY[] = "IsClearMAG";
        bool isContainsMAG = StringConvert::Contains(flagArray, "3");
        if(isContainsMAG && isAutoMAG)
        {
            if(!_magGate.IsEmpty())
            {
                ASSERT_FuncErrInfo(_pDev->Write(_magGate), DeviceError::SendErr, "转磁条口失败");
            }
            ASSERT_FuncInfo(_mag.MagRead(), "读磁条操作失败");

            arg.Put(IsClearMagKEY, ArgConvert::ToString(true));
        }

        Timer timer;
        while(timer.Elapsed() < timeoutMs)
        {
            for(size_t i = 0;i < flagArray.GetLength(); ++i)
            {
                if(InterruptBehavior::Implement(*this))
                {
                    ResetErr();
                    _logErr(DeviceError::OperatorInterruptErr);
                    return false;
                }
                
                atr.Clear();
                uid.Clear();
                trBuff.Clear();

                switch(flagArray[i])
                {
                case '1':
                case '4':
                    if(!_contactlessGate.IsEmpty())
                    {
                        ASSERT_FuncErrInfo(_pDev->Write(_contactlessGate), DeviceError::SendErr, "转非接口失败");
                    }
                    if(_contactlessIC.ActiveContactlessICCard(0, &cardType, &uid, &atr))
                    {
                        // 检测是否为身份证
                        if(cardType == 0x0B)
                        {
                            if(flagArray[i] == '1')
                                break;
                            
                            if(!_idGate.IsEmpty())
                            {
                                ASSERT_FuncErrInfo(_pDev->Write(_idGate), DeviceError::SendErr, "转二代证口失败");
                            }
                            if(_sdtapiAdapter.FindIDCard())
                            {
                                cardFlag = CardIDC;
                                rlt.Put(SLOT_KEY, ArgConvert::ToString<int>(cardFlag));
                                rlt.Put(SLOT_NAME_KEY, SlotToString(cardFlag));
                                rlt.Put("ATR", ArgConvert::ToString(atr));
                                rlt.Put("UID", ArgConvert::ToString(uid));
                                return true;
                            }
                        }
                        // 实际应该检测二代证
                        if(flagArray[i] == '4')
                            break;

                        cardFlag = CardContactless;
                        rlt.Put(SLOT_KEY, ArgConvert::ToString<int>(cardFlag));
                        rlt.Put(SLOT_NAME_KEY, SlotToString(cardFlag));
                        rlt.Put("ATR", ArgConvert::ToString(atr));
                        rlt.Put("UID", ArgConvert::ToString(uid));

                        return true;
                    }
                    break;
                case '2':
                    if(!_contactGate.IsEmpty())
                    {
                        ASSERT_FuncErrInfo(_pDev->Write(_contactGate), DeviceError::SendErr, "转接触口失败");
                    }
                    if(_contactIC.PowerOn(NULL, &atr))
                    {
                        cardFlag = CardContact;
                        rlt.Put(SLOT_KEY, ArgConvert::ToString<int>(cardFlag));
                        rlt.Put(SLOT_NAME_KEY, SlotToString(cardFlag));
                        rlt.Put("ATR", ArgConvert::ToString(atr));
                        return true;
                    }
                    break;
                case '3':
                    if(!_magGate.IsEmpty())
                    {
                        ASSERT_FuncErrInfo(_pDev->Write(_magGate), DeviceError::SendErr, "转磁条口失败");
                    }
                    DeviceError::ErrorCode err = H002MagDevAdapter::IsMagPresent(_pDev, trBuff);
                    if(DeviceError::IsSuccess(err))
                    {
                        if(isAutoMAG)
                        {
                            arg[IsClearMagKEY].Value = ArgConvert::ToString(false);
                        }

                        cardFlag = CardMAG;
                        rlt.Put(SLOT_KEY, ArgConvert::ToString<int>(cardFlag));
                        rlt.Put(SLOT_NAME_KEY, SlotToString(cardFlag));
                        rlt.Put("TrBuff", ArgConvert::ToString(trBuff));
                        return true;
                    }
                    break;
                }
                Timer::Wait(intervalMs);
            }
        }
        ResetErr();
        _logErr(DeviceError::WaitTimeOutErr, "等待放卡超时");
        return false;
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
} // nemespace driver
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_H002CARDDETECTORCMDDRIVER_H_
//========================================================= 